#include "cs8900.h"
#include "net.h"
#include "types.h"

#define CS8900_CTL (CS8900_BASE+0x05*CS8900_OFF)
#define CS8900_DATA (CS8900_BASE+0x06*CS8900_OFF)

static unsigned short get_reg_init_bus (int regno)
{
		/* force 16 bit busmode */
	unsigned char c;

	c = CS8900_BUS16_0;
	c = CS8900_BUS16_1;
	c = CS8900_BUS16_0;
	c = CS8900_BUS16_1;
	c = CS8900_BUS16_0;

	CS8900_PPTR = regno;
	return (unsigned short) CS8900_PDATA;
}


static void put_reg (int regno, unsigned short val)
{
	CS8900_PPTR = regno;
	CS8900_PDATA = val;
}

static void eth_reginit (void)
{
		/* receive only error free packets addressed to this card */
	put_reg (PP_RxCTL, PP_RxCTL_IA | PP_RxCTL_Broadcast | PP_RxCTL_RxOK);
		/* do not generate any interrupts on receive operations */
	put_reg (PP_RxCFG, 0);
		/* do not generate any interrupts on transmit operations */
	put_reg (PP_TxCFG, 0);
		/* do not generate any interrupts on buffer operations */
	put_reg (PP_BufCFG, 0);
		/* enable transmitter/receiver mode */
	put_reg (PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);
}


unsigned short get_reg (int regno)
{
	CS8900_PPTR = regno;
	return (unsigned short) CS8900_PDATA;
}

static void eth_reset (void)
{
#if 0
	int tmo;
#endif
	unsigned short us;

		/* reset NIC */
	put_reg (PP_SelfCTL, get_reg (PP_SelfCTL) | PP_SelfCTL_Reset);

#if 0
		/* wait for 200ms */
	udelay (200000);
		/* Wait until the chip is reset */

	tmo = get_timer (0) + 1 * CFG_HZ;
	while ((((us = get_reg_init_bus (PP_SelfSTAT)) & PP_SelfSTAT_InitD) == 0)
		&& tmo < get_timer (0))
		/*NOP*/;
#endif
	do {
		us = get_reg_init_bus (PP_SelfSTAT);
		us = us & PP_SelfSTAT_InitD;
	} while (us == 0);
	
}

void eth_halt( void )
{
		/* disable transmitter/receiver mode */
    put_reg(PP_LineCTL, 0);

		/* "shutdown" to show ChipID or kernel wouldn't find he cs8900 ... */
    get_reg_init_bus(PP_ChipID);
}

int eth_init(bd_t * bd)
{
    unsigned short chipid;

    chipid = get_reg(PP_ChipID);        
	if (chipid != 0x630e) {
        printk ("CS8900 Ethernet chip not found?, chipid = %hx!\n"
            , chipid);
		return 0;

    }

	eth_reset ();
		/* set the ethernet address */
	put_reg (PP_IA + 0, bd->bi_enetaddr[0] | (bd->bi_enetaddr[1] << 8));
	put_reg (PP_IA + 2, bd->bi_enetaddr[2] | (bd->bi_enetaddr[3] << 8));
	put_reg (PP_IA + 4, bd->bi_enetaddr[4] | (bd->bi_enetaddr[5] << 8));

	eth_reginit ();
	return 0;
}

/* Send a data block via Ethernet. */
int eth_send(void *packet, int length)
{
    unsigned short *addr;
#if 0
	int tmo;
#endif
	unsigned short s;

 retry:
		/* initiate a transmit sequence */
    CS8900_TxCMD = PP_TxCmd_TxStart_Full;
    CS8900_TxLEN = length;
    
		/* Test to see if the chip has allocated memory for the packet */
    if ((get_reg(PP_BusSTAT) & PP_BusSTAT_TxRDY) == 0) {    
			/* Oops... this should not happen! */
		printk("cs: unable to send packet; retrying...\n");
#if 0
		for (tmo = get_timer(0) + 5 * CFG_HZ; get_timer(0) < tmo; )
			/*NOP*/;
#endif
		eth_reset();
		goto retry; 
    }

		/* Write the contents of the packet */
		/* assume even number of bytes */
    for(addr = packet; length > 0; length -= 2 )
		CS8900_RTDATA = *addr++;

		/* wait for transfer to succeed */
#if 0
	tmo = get_timer(0) + 5 * CFG_HZ;
#endif
	while((s = get_reg(PP_TER) & ~0x1F) == 0) 
    {
#if 0
		if (get_timer(0) >= tmo)
			break;
#endif
	}
     
	/* nothing */ ;
    if ((s & (PP_TER_CRS | PP_TER_TxOK)) != PP_TER_TxOK) {
		printk("\ntransmission error %#x\n", s);
    }

    return 0;
}

extern int eth_rx(void)
{
    int i;
    unsigned short rxlen;
    unsigned int *addr;
    unsigned short status;

    status = get_reg(PP_RER);

    if ((status & PP_RER_RxOK) == 0) {
        return 0;
    }

    __asm__ __volatile__ (
        "ldr %0, [%1];"
        :"=r"(i): "r"(0x19000300), "r"(0xffff)
        );
    rxlen = i >> 16;
    status = i & 0xffff;
    
    if(rxlen > PKTSIZE_ALIGN + PKTALIGN)
      printk("packet too big!\n");

    
    addr = (unsigned int *)NetRxPackets[0];

    for(i = (rxlen+4) >> 2; i > 0; i--) {
//      *addr++ = htons(CS8900_RTDATA);
        __asm__ __volatile__ (
            "ldr %0, [%1];"
            :"=r"(*addr): "r"(0x19000300), "r"(0xffff)
            );
        ++addr;
    }
/*
    for (i = 0; i < (rxlen); ++i) {
        if (i >= 0x100)
            break;
        if (i % 0x10 == 0)
            printk("\n");
        printk("%02x ", NetRxPackets[0][i]);
    }
    printk("\n");
    for(;;);
*/
    /* Pass the packet up to the protocol layers. */
    NetReceive(NetRxPackets[0], rxlen);

    return rxlen;
}
